<?php

class TypesExporter implements iTypesExporter {
	
	private
		$aTypes = array(),
		$aRequiredTypes = array(),
		$aConnections = array(),
		$aImagesProfiles = array(),
		$aGuides = array();
	
	public function __construct() {}
	
	/**
	 * Добавить тип
	 * @param Type $oType
	 * @return void
	 */
	public function addType(Type $oType) {
		$this->aTypes[] = $oType;
	}
	
	/**
	 * @return DOMDocument
	 */
	public function getXMLData() {
		$xml = new DOMDocument ('1.0', 'utf-8');
		
		$datatypesNode = $xml->createElement('datatypes');
		$datatypesNode->setAttribute('version', '1.0');
		
		$aTypesFields = array();
		$aRequiredTypes = array();
		$aTypesNodes = array();
		
		foreach ($this->aTypes as $oType) {
			$typeName = $oType->getName();
			$aTypesFields[$typeName] = array();
			$aFieldsNames = $oType->getFieldsNames();
			
			foreach ($aFieldsNames as $fieldName) {
				$aTypesFields[$typeName][] = $fieldName;
			}
			
			$aTypesNodes[] = $this->getType($oType, $xml);
		}
		
		//$typesNode->appendChild($xml->createElement('names', implode(',', $aTypesNames)));
		
		// Информация о типах и их полях, которые экспортируются
		$importNode = $xml->createElement('import-types');
		
		foreach ($aTypesFields as $typeName => $aFields) {
			$typeNode = $xml->createElement('type');
			$typeNode->setAttribute('name', $typeName);
			
			foreach ($aFields as $fieldName) {
				$typeNode->appendChild($xml->createElement('field', $fieldName));
			}
			
			$importNode->appendChild($typeNode);
		}
		
		$datatypesNode->appendChild($importNode);
		
		
		// Требуемые типы и поля
		$requiredTypesNode = $xml->createElement('required-types');
		
		foreach ($this->aRequiredTypes as $typeName => $aFieldsNames) {
			$typeNode = $xml->createElement('type');
			$typeNode->setAttribute('name', $typeName);

			foreach ($aFieldsNames as $fieldName) {
				$typeNode->appendChild($xml->createElement('field', $fieldName));
			}
			
			$requiredTypesNode->appendChild($typeNode);
		}
		
		$datatypesNode->appendChild($requiredTypesNode);
		
		
		// Связи
		$connectionsNode = $xml->createElement('types-connections');
		
		foreach ($this->aConnections as $aConnection) {
			$connectionsNode->appendChild($this->getConnection($aConnection['object'], $aConnection['key'], $xml));
		}
		
		$datatypesNode->appendChild($connectionsNode);
		
		
		// Профили изображений
		$imgProfNode = $xml->createElement('images-profiles');
		
		foreach ($this->aImagesProfiles as $key => $oImgProf) {
			$imgProfNode->appendChild($this->getImgProf($oImgProf, $key, $xml));
		}
		
		$datatypesNode->appendChild($imgProfNode);
		
		$typesNode = $xml->createElement('types');
		
		
		// Справочники
		$guidesNode = $xml->createElement('guides');
		
		foreach ($this->aGuides as $typeName => $labelFieldName) {
			$typeNode = $xml->createElement('type');
			$typeNode->setAttribute('name', $typeName);
			$typeNode->setAttribute('label-field', $labelFieldName);
			
			$guidesNode->appendChild($typeNode);
		}
		
		$datatypesNode->appendChild($guidesNode);
		
		
		// Типы данных
		foreach ($aTypesNodes as $typeNode) {
			$typesNode->appendChild($typeNode);
		}
		
		$datatypesNode->appendChild($typesNode);
		
		$xml->appendChild($datatypesNode);
		
		return $xml;
	}
	
	
	/**
	 * @param Type $oType
	 * @param DOMDocument $xml
	 * @return DOMElement
	 */
	private function getType(Type $oType, DOMDocument $xml) {
		$typeNode = $xml->createElement('type');
		$typeNode->setAttribute('name', $oType->getName());
		
		$typeProperties = $xml->createElement('properties');
		
		//$typeProperties->appendChild($xml->createElement('name', $oType->getName()));
		$typeProperties->appendChild($xml->createElement('title', $oType->getTitle()));
		$typeProperties->appendChild($xml->createElement('table-name', $oType->getTableName()));
		$typeProperties->appendChild($xml->createElement('parent-id', $oType->getParentId()));
		
		$db = DBController::getInstance();
		$getCreate = $db->query ("SHOW CREATE TABLE `" . DB_NAME . "`.`" . $oType->getTableName() . "`");
		list (,$strCreate) = $db->fetch_row ($getCreate);
		$strCreate = str_replace ("\n", " ", $strCreate);

		$typeProperties->appendChild($xml->createElement('create-table', $strCreate));
		
		$typeNode->appendChild($typeProperties);
		
		$typeNode->appendChild($this->getTypeFiedls($oType, $xml));
		
		return $typeNode;
	}
	
	/**
	 * @param Type $oType
	 * @param DOMDocument $xml
	 * @return DOMElement
	 */
	private function getTypeFiedls(Type $oType, DOMDocument $xml) {
		$fieldsNode = $xml->createElement('fields');
		
		$aFields = $oType->getFields();
		
		foreach ($aFields as $oField) {
			$fieldsNode->appendChild($this->getField($oField, $xml));
		}
		
		return $fieldsNode;
	}
	
	/**
	 * @param Field $oField
	 * @param DOMDocument $xml
	 * @return DOMElement
	 */
	private function getField(Field $oField, DOMDocument $xml) {
		$fieldNode = $xml->createElement('field');
		$fieldNode->setAttribute('name', $oField->getName());
		
		$character = $oField->getCharacter();
		
		//$fieldNode->appendChild($xml->createElement('name', $oField->getName()));
		$fieldNode->appendChild($xml->createElement('locked', $oField->isLocked() ? 1 : 0));
		$fieldNode->appendChild($xml->createElement('required', $oField->isRequired() ? 1 : 0));
		$fieldNode->appendChild($xml->createElement('title', $oField->getTitle()));
		$fieldNode->appendChild($xml->createElement('character', $character));
		$fieldNode->appendChild($xml->createElement('editable', $oField->isEditable() ? 1 : 0));
		$fieldNode->appendChild($xml->createElement('help', $oField->getHelp()));
		$fieldNode->appendChild($xml->createElement('error', $oField->getErrorMessage()));
		$fieldNode->appendChild($xml->createElement('position', $oField->getPosition()));
		$fieldNode->appendChild($xml->createElement('allow-search', $oField->isSearch() ? 1 : 0));
		$fieldNode->appendChild($xml->createElement('maxlength', $oField->getMaxlength()));
		
		if ($character == 'object_value') {
			$connectionId = $oField->getConnectionId();
			
			if (!isset($this->aConnections[$connectionId])) {
				$this->aConnections[$connectionId] = $this->getConnectionObject($connectionId, $oField->getTypeId());
			}
			
			$fieldNode->appendChild($xml->createElement('connection-key', $this->aConnections[$connectionId]['key']));
			
			$oConnectedField = FieldsController::getInstance()->getField($oField->getAddId());
			
			$fieldNode->appendChild($xml->createElement('connected-field', $oConnectedField->getName()));
		} elseif ($character == 'guide' || $character == 'multiguide') {
			$typesController = TypesController::getInstance();
			
			$oGuide = ObjectsController::getInstance()->getObject($typesController->getTypeByName('explay_guide'), $oField->getAddId());
			
			$labelFieldName = FieldsController::getInstance()->getField($oGuide->getValue('label_field_id'))->getName();
			
			$guideTypeName = $typesController->getTypeById($oGuide->getValue('type_id'))->getName();
			
			if (!isset($this->aGuides[$guideTypeName])) {
				$this->aGuides[$guideTypeName] = $labelFieldName;
			}
			
			/*if (!in_array($guideTypeName, $this->aRequiredTypesNames)) {
				$this->aRequiredTypesNames[] = $guideTypeName;
			}*/
			if (!isset($this->aRequiredTypes[$guideTypeName])) {
				$this->aRequiredTypes[$guideTypeName] = array();
			}
			
			$this->aRequiredTypes[$guideTypeName][] = $labelFieldName;
			
			$fieldNode->appendChild($xml->createElement('guide-type', $guideTypeName));
		} elseif ($character == 'image') {
			$imgProfKey = 'img_' . $oField->getAddId();
			
			if (!isset($this->aImagesProfiles[$imgProfKey])) {
				$this->aImagesProfiles[$imgProfKey] = ObjectsController::getInstance()->getObject(TypesController::getInstance()->getTypeByName('image_profile'), $oField->getAddId());
			}
			
			$fieldNode->appendChild($xml->createElement('image-profile', $imgProfKey));
		}
		
		return $fieldNode;
	}
	
	private function getConnection(Object $oConnection, $key, DOMDocument $xml) {
		$typesController = TypesController::getInstance();
		$fieldsController = FieldsController::getInstance();
		
		$connectionNode = $xml->createElement('connection');
		$connectionNode->setAttribute('key', $key);
		
		$parentTypeName = $typesController->getTypeById($oConnection->getValue('parent_type_id'))->getName();
		$childTypeName = $typesController->getTypeById($oConnection->getValue('child_type_id'))->getName();
		
		if ($oConnection->getValue('parent_field_id')) {
			$parentFieldName = $fieldsController->getField($oConnection->getValue('parent_field_id'))->getName();
		} else {
			$parentFieldName = 'id';
		}
		
		$childFieldName = $fieldsController->getField($oConnection->getValue('child_rel_field_id'))->getName();
		
		$connectionNode->appendChild($xml->createElement('parent-type', $parentTypeName));
		$connectionNode->appendChild($xml->createElement('parent-field', $parentFieldName));
		$connectionNode->appendChild($xml->createElement('child-type', $childTypeName));
		$connectionNode->appendChild($xml->createElement('child-field', $childFieldName));
		$connectionNode->appendChild($xml->createElement('connection-type', $oConnection->getValue('connection_type')));
		$connectionNode->appendChild($xml->createElement('alias', $oConnection->getValue('alias')));
		
		return $connectionNode;
	}
	
	private function getImgProf(Object $oImgProf, $key, DOMDocument $xml) {
		$node = $xml->createElement('image-profile');
		$node->setAttribute('key', $key);
		
		$node->appendChild($xml->createElement('title', $oImgProf->getValue('title')));
		$node->appendChild($xml->createElement('max-width', $oImgProf->getValue('max_width')));
		$node->appendChild($xml->createElement('max-height', $oImgProf->getValue('max_height')));
		$node->appendChild($xml->createElement('exit-type', $oImgProf->getValue('exit_type')));
		$node->appendChild($xml->createElement('dir', $oImgProf->getValue('dir')));
		
		return $node;
	}
	
	private function getConnectionObject($connectionId, $calledTypeId) {
		$typesController = TypesController::getInstance();
		$fieldsController = FieldsController::getInstance();
		
		$aConnection = array();
		
		$oConnection = $typesController->getConnection($connectionId);
		
		if ($oConnection->getValue('parent_type_id') == $calledTypeId) {
			$requiredTypeName = $typesController->getTypeById($oConnection->getValue('child_type_id'))->getName();
		} else {
			$requiredTypeName = $typesController->getTypeById($oConnection->getValue('parent_type_id'))->getName();
		}
		
		/*if (!in_array($requiredTypeName, $this->aRequiredTypesNames)) {
			$this->aRequiredTypesNames[] = $requiredTypeName;
		}*/
		
		if (!isset($this->aRequiredTypes[$requiredTypeName])) {
			$this->aRequiredTypes[$requiredTypeName] = array();
		}
		
		$aConnection['object'] = $oConnection;
		
		$parentTypeName = $typesController->getTypeById($oConnection->getValue('parent_type_id'))->getName();
		$childTypeName = $typesController->getTypeById($oConnection->getValue('child_type_id'))->getName();
		
		if ($oConnection->getValue('parent_field_id')) {
			$parentFieldName = $fieldsController->getField($oConnection->getValue('parent_field_id'))->getName();
		} else {
			$parentFieldName = 'id';
		}
		
		$childFieldName = $fieldsController->getField($oConnection->getValue('child_rel_field_id'))->getName();
		
		if ($oConnection->getValue('parent_type_id') == $calledTypeId) {
			$this->aRequiredTypes[$requiredTypeName][] = $childFieldName;
		}
		
		$aConnection['key'] = $parentTypeName . '_' . $parentFieldName . '__' . $childTypeName . '_' . $childFieldName;
		
		return $aConnection;
	}
}
